using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using System.Reflection;
using INI;
using MySql.Data.MySqlClient;

using FullMotion.LiveForSpeed.InSim;
using FullMotion.LiveForSpeed.InSim.Enums;
using FullMotion.LiveForSpeed.InSim.Events;

using System.Data.SqlClient;

namespace FGServer
{
    class Program
    {
        // Configuration changes start here
        private string ip;
        private int port;
        private string adminPass;
        private Boolean useTCP;
        private char prefix;
        private Boolean demohost;
        private Boolean configAgreement;
        private string configAgreementFile;
        private Boolean configSpectateStationary;
        private int configSpectateTime;
        private Boolean configVoteCancelling;
        private Boolean configBlueFlagWarn;
        private Boolean configSpectateSpeeders;
        private Boolean configAutoRestart;
        private TimeSpan restarttime;
        private Boolean configRaceStartMessage;
        private string configRaceStartMessageFile;
        private Boolean configCheckSwearwords;
        private Boolean configSpamming;
        private int configSpammingNum;
        private Boolean configHandicaps;
        private string configHandicapFile;
        private Boolean configDebugMessages;
        private int configVoteCancelPlayers;
        private int allowedControllers;
        private string controllerUsed;
        private int numPlayers;
        private string raceState;
        private Boolean configRestrictions;
        private string configRestrictionsFile;
        // Configuration changes stop here

        private Boolean RaceFinished = false;
        private quotes q = new quotes();
        private handicap h = new handicap();
        private restrictions r = new restrictions();
        private string fgTitle;
        private string fgVersion;
        private string fgCompany;
        private Boolean AllowRestart = true;

        private InSimHandler handler = new InSimHandler();
        private Hashtable database = new Hashtable();
        private ArrayList swearwords = new ArrayList();

        private LfsButton restartbutton = new LfsButton(0);
        private LfsButton Agree = new LfsButton(1);
        private LfsButton DontAgree = new LfsButton(2);
        private int agreementlines;
        private LfsButton[] agreement;

        // test mysql area
        //static MySqlCommand cmd = new MySqlCommand();
        //static Object returnValue;
        //static MySqlConnection connect = new MySqlConnection("Database=test;Server=192.168.15.10;User Id=root;Password=bur07nsy;");
        
        // end area

        /// <summary>
        /// Starts up the program
        /// </summary>
        /// <param name="args">The args.</param>
        static void Main(string[] args)
        {
            Program p = new Program();
            p.Config();
            p.Setup();
            p.Start();
            p.Goooo();
        }

        /// <summary>
        /// Gets configuration from ini file.
        /// </summary>
        public void Config()
        {

            //try
            //{
            //    connect.Open();
            //}
            //catch (Exception ex)
            //{
            //    Console.WriteLine("Error Message: " + ex.Message);
            //    Console.WriteLine("Message Source: " + ex.Source);
            //    Console.ReadLine();
            //    Environment.Exit(0);
            //}

            IniFile ini = new IniFile(".\\config.ini");
            ip = ini.IniReadValue("server", "ip");
            port = Convert.ToInt32(ini.IniReadValue("server", "port"));
            adminPass = ini.IniReadValue("server", "admin pass");
            useTCP = Convert.ToBoolean(ini.IniReadValue("server", "use tcp"));
            prefix = Convert.ToChar(ini.IniReadValue("server", "insim prefix"));
            demohost = Convert.ToBoolean(ini.IniReadValue("server", "demohost"));
            configAgreement = Convert.ToBoolean(ini.IniReadValue("agreement", "show agreement"));
            configAgreementFile = ini.IniReadValue("agreement", "file location");
            agreementlines = Convert.ToInt16(ini.IniReadValue("agreement", "number of lines"));
            agreement = new LfsButton[agreementlines];
            configSpectateStationary = Convert.ToBoolean(ini.IniReadValue("idle", "spectate stationary"));
            configSpectateTime = Convert.ToInt16(ini.IniReadValue("idle", "spectate time"));
            configVoteCancelling = Convert.ToBoolean(ini.IniReadValue("server", "vote cancelling"));
            configBlueFlagWarn = Convert.ToBoolean(ini.IniReadValue("server", "blue flag warn"));
            configSpectateSpeeders = Convert.ToBoolean(ini.IniReadValue("speeders", "spectate speeders"));
            configAutoRestart = Convert.ToBoolean(ini.IniReadValue("auto restart", "auto restart"));
            configVoteCancelPlayers = Convert.ToInt16(ini.IniReadValue("server", "num players"));
            int hours = Convert.ToInt16(ini.IniReadValue("auto restart", "hours"));
            int min = Convert.ToInt16(ini.IniReadValue("auto restart", "min"));
            int sec = Convert.ToInt16(ini.IniReadValue("auto restart", "sec"));
            restarttime = new TimeSpan(hours, min, sec);
            configRaceStartMessage = Convert.ToBoolean(ini.IniReadValue("race start message", "enabled"));
            configRaceStartMessageFile = ini.IniReadValue("race start message", "file location");
            configCheckSwearwords = Convert.ToBoolean(ini.IniReadValue("server", "check for swearwords"));
            configSpamming = Convert.ToBoolean(ini.IniReadValue("server", "check for spamming"));
            configSpammingNum = Convert.ToInt16(ini.IniReadValue("server", "number of chats"));
            configHandicaps = Convert.ToBoolean(ini.IniReadValue("handicaps","handicap checker"));
            configHandicapFile = ini.IniReadValue("handicaps", "file location");
            configDebugMessages = Convert.ToBoolean(ini.IniReadValue("server", "debug"));
            allowedControllers = Convert.ToInt16(ini.IniReadValue("controllers", "level allowed"));
            configRestrictions = Convert.ToBoolean(ini.IniReadValue("restrictions", "enabled"));
            configRestrictionsFile = ini.IniReadValue("restrictions", "file location");
        }

        /// <summary>
        /// Setups the server control parameters.
        /// </summary>
        public void Setup()
        {
            if (configHandicaps)
            {
                h.ReadHandicaps(configHandicapFile);
            }

            if (configRestrictions)
            {
                r.ReadRestrictions(configRestrictionsFile);
            }

            database = Hashtable.Synchronized(database); 
            LoadSwearwords();

            // Auto restart system button parameters
            restartbutton.Text = "";
            restartbutton.Left = 80;
            restartbutton.Top = 10;
            restartbutton.Width = 50;
            restartbutton.Height = 10;
            restartbutton.TextAlignment = ButtonTextAlignment.Center;
            restartbutton.Color = ButtonColor.Dark;
            restartbutton.TextColor = ButtonTextColor.TitleColor;
            // -->

            // Agreement function Yes/No buttons
            Agree.Text = "Yes";
            Agree.Left = 20;
            Agree.TextAlignment = ButtonTextAlignment.Center;
            Agree.Height = 10;
            Agree.Color = ButtonColor.Light;
            Agree.TextColor = ButtonTextColor.Ok;
            Agree.Width = 20;
            Agree.Top = (byte)(10 + (agreement.Length * 5));
            Agree.Clickable = true;

            DontAgree.Text = "No";
            DontAgree.Left = (byte)(Agree.Left + Agree.Width + 1);
            DontAgree.Top = (byte)(10 + (agreement.Length * 5));
            DontAgree.Width = 20;
            DontAgree.TextAlignment = ButtonTextAlignment.Center;
            DontAgree.Height = 10;
            DontAgree.Color = ButtonColor.Light;
            DontAgree.TextColor = ButtonTextColor.Cancel;
            DontAgree.Clickable = true;
            // -->

            byte buttoncount = 3;


            try
            {
                StreamReader re = File.OpenText(configAgreementFile);

                for (int i = 0; i < this.agreementlines; i++)
                {
                    agreement[i] = new LfsButton(buttoncount);
                    agreement[i].Width = 160;
                    agreement[i].Left = 20;
                    agreement[i].Top = (byte)(10 + (i * 5));
                    agreement[i].Height = 5;
                    agreement[i].TextAlignment = ButtonTextAlignment.Left;
                    agreement[i].Color = ButtonColor.Dark;
                    agreement[i].TextColor = ButtonTextColor.SelectedText;

                    string input = "";
                    if ((input = re.ReadLine()) != null)
                    {
                        if (input.StartsWith("[H]"))
                        {
                            agreement[i].TextColor = ButtonTextColor.TitleColor;
                            input = input.Substring(3);
                        }
                        agreement[i].Text = input;
                    }
                    buttoncount++;
                }

                re.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.Source);
                Console.ReadLine();
                Environment.Exit(0);
            }        

            try
            {
                q.loadQuotes(configRaceStartMessageFile);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.Source);
                Console.ReadLine();
                Environment.Exit(0);
            }

            handler.Configuration.LFSHost = ip;
            handler.Configuration.LFSHostPort = port;
            handler.Configuration.AdminPass = adminPass;
            handler.Configuration.UseTCP = useTCP;
            handler.Configuration.ProgramName = "LFS Server Control";
            handler.Configuration.UseSplitMessages = true;
            handler.Configuration.Prefix = prefix;
            handler.Configuration.MultiCarTracking = false;
            handler.Configuration.TrackingInterval = 1000;
            handler.Configuration.ReconnectRetry = 10;
            handler.Configuration.AutoReconnect = true;

            // Any LFS event
            handler.LFSState += new FullMotion.LiveForSpeed.InSim.EventHandlers.StateEventHandler(handler_LFSState);

            // A racer joins the server
            handler.RaceTrackConnection += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackNewConnectionHandler(handler_RaceTrackConnection);

            // A racer leaves the server
            handler.RaceTrackConnectionLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackConnectionLeaveHandler(handler_RaceTrackConnectionLeave);

            // A message recieved from a player
            handler.LFSMessage += new FullMotion.LiveForSpeed.InSim.EventHandlers.MessageEventHandler(handler_LFSMessage);

            // The race start event
            handler.RaceTrackRaceStart += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackRaceStartHandler(handler_RaceTrackRaceStart);

            // Reordering of the grid at race start
            handler.RaceTrackReorder += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackReorderHandler(handler_RaceTrackReorder);

            // A racer joins the race/qual
            handler.RaceTrackPlayer += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerHandler(handler_RaceTrackPlayer);

            // A racer leaves the race/qual
            handler.RaceTrackPlayerLeave += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLeaveHandler(handler_RaceTrackPlayerLeave);

            // 
            handler.RaceTrackPlayerChange += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerChangeHandler(handler_RaceTrackPlayerChange);

            // A racer goes to the pit
            handler.RaceTrackPlayerPits += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitsHandler(handler_RaceTrackPlayerPits);

            // A racer renames himself
            handler.RaceTrackPlayerRename += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerRenameHandler(handler_RaceTrackPlayerRename);

            // A racer caused a Yellow or was given a Blue flag
            handler.RaceTrackPlayerFlag += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerFlagHandler(handler_RaceTrackPlayerFlag);

            // A racer got a penalty
            handler.RaceTrackPlayerPenalty += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPenaltyHandler(handler_RaceTrackPlayerPenalty);

            // A racer enters/exits the pitlane
            handler.RaceTrackPlayerPitlane += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitlaneHandler(handler_RaceTrackPlayerPitlane);

            // A racer finishes the race
            handler.RaceTrackPlayerFinish += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerFinishHandler(handler_RaceTrackPlayerFinish);

            // A racer votes to restart/qual/end
            handler.Vote += new FullMotion.LiveForSpeed.InSim.EventHandlers.VoteEventHandler(handler_Vote);

            // A racer's information on track; speed, angle, etc.
            handler.RaceTrackMultiCarInfo += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackMultiCarInfoHandler(handler_RaceTrackMultiCarInfo);

            // A racer's split number, time, etc when they cross a sector split marker
            handler.RaceTrackPlayerSplitTime += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerSplitTimeHandler(handler_RaceTrackPlayerSplitTime);

            // A racer's laptime, lap number, etc when they cross the start/finish line
            handler.RaceTrackPlayerLap += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerLapHandler(handler_RaceTrackPlayerLap);

            // A racer starts a pitstop
            handler.RaceTrackPlayerPitStopBegin += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitStopBeginHandler(handler_RaceTrackPlayerPitStopBegin);

            // A racer ends a pitstop
            handler.RaceTrackPlayerPitStopFinish += new FullMotion.LiveForSpeed.InSim.EventHandlers.RaceTrackPlayerPitStopFinishHandler(handler_RaceTrackPlayerPitStopFinish);
            
            handler.ButtonClick += new FullMotion.LiveForSpeed.InSim.EventHandlers.ButtonClickHandler(handler_ButtonClick);
        }


        void handler_RaceTrackPlayerChange(InSimHandler sender, RaceTrackPlayerChange e)
        {
            if (database.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer racer = (lfsracer)database[(getConnectionid(e.PlayerId))];
                
                switch (allowedControllers)
                {
                    case 0:
                        break;
                    case 1:
                        if (e.KeyboardNoHelp)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("Keyboard Stabilized or better is required on this server.", racer.connectionid);
                            controllerUsed = "Keyboard No Help";
                        }
                        break;
                    case 2:
                        if (e.KeyboardNoHelp)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", racer.connectionid);
                            controllerUsed = "Keyboard No Help";
                        }
                        if (e.KeyboardStabilized)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", racer.connectionid);
                            controllerUsed = "Keyboard Stabilized";
                        }
                        break;
                    case 3:
                        if (e.KeyboardNoHelp)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A wheel is required on this server.", racer.connectionid);
                            controllerUsed = "Keyboard No Help";
                        }
                        if (e.KeyboardStabilized)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", racer.connectionid);
                            controllerUsed = "Keyboard Stabilized";
                        }
                        if (e.Mouse)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", racer.connectionid);
                            controllerUsed = "Mouse";
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// A racer's laptime, lap number, etc when they cross the start/finish line
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerLap(InSimHandler sender, RaceTrackPlayerLap e)
        {
            lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
            r.lapnumber = e.LapsDone;
            if (configVoteCancelling)
            {
                if ((e.LapsDone == 2) && (AllowRestart) && (!RaceFinished) && (numPlayers >= configVoteCancelPlayers))
                {
                    handler.SendMessage(TextColor.Yellow + "No voting from now on");
                    AllowRestart = false;
                }
            }
            //Auto Spectate function
            if (configSpectateStationary)
            {
                r.Idle = 0; // 0 out their Autospectate idle number
            }
        }

        /// <summary>
        /// A racer's split number, time, etc when they cross a sector split marker
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerSplitTime(InSimHandler sender, RaceTrackPlayerSplitTime e)
        {
            //Auto Spectate function
            if (configSpectateStationary)
            {
                if (database.ContainsKey(getConnectionid(e.PlayerId)))
                {
                    lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                    r.Idle = 0; // 0 out their Autospectate idle number
                }
            }
        }

        /// <summary>
        /// Agreement Yes/No buttons
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_ButtonClick(InSimHandler sender, ButtonClick e)
        {
            if (e.ButtonId == 1) // The racer clicked "Yes" (id 1)
            {
                for (int i = 1; i <= (agreementlines + 2); i++)
                {
                    handler.DeleteButton((byte)i, e.ConnectionId);    
                }
                
                if (database.ContainsKey(e.ConnectionId))
                {
                    lfsracer r = (lfsracer)database[e.ConnectionId];
                    handler.SendMessageToConnection("Enjoy your stay :)", r.connectionid);
                    r.signedAgreement = true;
                }
            }
            if (e.ButtonId == 2) // The racer clicked "No" 
            {
                for (int i = 1; i <= (agreementlines+2); i++)
                {
                    handler.DeleteButton((byte)i, e.ConnectionId);
                }

                if (database.ContainsKey(e.ConnectionId))
                {
                    lfsracer r = (lfsracer)database[e.ConnectionId];
                    if (demohost)
                    {
                        handler.SendMessageToConnection("Bye", r.connectionid);
                        handler.SendMessage("/kick " + r.playername);
                    }
                    else
                    {
                        handler.SendMessageToConnection("Bye", r.connectionid);
                        handler.SendMessage("/kick " + r.username);
                    }
                }
            }
        }

        /// <summary>
        /// A racer's information on track; speed, angle, etc.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackMultiCarInfo(InSimHandler sender, RaceTrackMultiCarInfo e)
        {

        }

        /// <summary>
        /// Reordering of the grid at race start
        /// Used for the Auto Restart function
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackReorder(InSimHandler sender, RaceTrackReorder e)
        {
            handler.DeleteButton(0, false);
        }

        /// <summary>
        /// A racer renames himself
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerRename(InSimHandler sender, RaceTrackPlayerRename e)
        {
            if (database.ContainsKey(e.ConnectionId))
            {
                lfsracer r = (lfsracer)database[(e.ConnectionID)];
                r.playername = e.PlayerName;
            }
        }

        /// <summary>
        /// A racer votes to restart/qual/end
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="FullMotion.LiveForSpeed.InSim.Events.VoteEventArgs"/> instance containing the event data.</param>
        void handler_Vote(InSimHandler sender, VoteEventArgs e)
        {
            // Vote cancelling function
            if ((e is VoteAction) && (configVoteCancelling) && (!AllowRestart) && (numPlayers >= configVoteCancelPlayers) && (raceState == "Race"))
            {
                if (configDebugMessages)
                {
                    Console.WriteLine("Vote Cancelled");
                }
                handler.SendMessage("/me says restarting is not allowed until the race is finished");
                handler.CancelVote();
                return;
            }

            if (e is VoteCancel)
            {
            }
        }

        /// <summary>
        /// A racer caused a Yellow or was given a Blue flag
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerFlag(InSimHandler sender, RaceTrackPlayerFlag e)
        {
            // Blue flag warning to racer
            if ((e.Flag == RaceFlag.Blue) && (e.Active) && (configBlueFlagWarn))
            {
                if (database.ContainsKey(getConnectionid(e.PlayerId)) && database.ContainsKey(getConnectionid(e.ObstructedPlayerId)))
                {
                    lfsracer blueflagracer = (lfsracer)database[(getConnectionid(e.PlayerId))];
                    lfsracer fasterracer = (lfsracer)database[(getConnectionid(e.ObstructedPlayerId))];
                    handler.SendMessageToPlayer(TextColor.Yellow + "Let " + fasterracer.playername + TextColor.Yellow + " pass!", blueflagracer.playerid);
                    if (configDebugMessages)
                    {
                        Console.WriteLine(blueflagracer.playername + " was shown blue flag message");
                    }
                }
            }
        }

        /// <summary>
        /// A racer enters/exits the pitlane
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerPitlane(InSimHandler sender, RaceTrackPlayerPitlane e)
        {
            if ((database.ContainsKey(getConnectionid(e.PlayerId))))
            {
                lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                if (e.EventType == Pitlane.Enter)
                {
                    if (configDebugMessages)
                    {
                        Console.WriteLine(r.playername + " entered the pitlane");
                    }
                }
                if (e.EventType == Pitlane.Exit)
                {
                    if (configDebugMessages)
                    {
                        Console.WriteLine(r.playername + " exited the pitlane");
                    }
                }
            }
        }

        /// <summary>
        /// Handler_s the race track player pit stop finish.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerPitStopFinish(InSimHandler sender, RaceTrackPlayerPitStopFinish e)
        {
            if ((database.ContainsKey(getConnectionid(e.PlayerId))))
            {
                lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                r.isracing = true;
                r.Idle = 0;
                if (configDebugMessages)
                {
                    Console.WriteLine(r.playername + " finished a pitstop");
                }
            }
        }

        /// <summary>
        /// Handler_s the race track player pit stop begin.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerPitStopBegin(InSimHandler sender, RaceTrackPlayerPitStopBegin e)
        {
            if ((database.ContainsKey(getConnectionid(e.PlayerId))))
            {
                lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                r.isracing = false;
                r.Idle = 0;
                if (configDebugMessages)
                {
                    Console.WriteLine(r.playername + " made a pitstop");
                }
            }
        }

        /// <summary>
        /// A racer got a penalty
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerPenalty(InSimHandler sender, RaceTrackPlayerPenalty e)
        {
            // Spectate racers who speed in pitlane
            if ((e.Reason.Equals(PenaltyReason.Speeding)) && (configSpectateSpeeders))
            {
                if (database.ContainsKey(getConnectionid(e.PlayerId)))
                {
                    lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                    if (r.lapnumber >= 1)
                    {
                        handler.SendMessageToPlayer(TextColor.Yellow + "Remember, the pitlane speed limit is 80 kph or 48 mph", r.playerid);
                    }
                    else
                    {
                        if (demohost)
                        {
                            handler.SendMessage("/spec " + r.playername);
                        }
                        else
                        {
                            handler.SendMessage("/spec " + r.username);
                        }
                        handler.SendMessageToPlayer(TextColor.Yellow + "You were spectated for speeding in the pitlane", r.playerid);
                        handler.SendMessageToPlayer(TextColor.Yellow + "The pitlane speed limit is 80 kph or 48 mph", r.playerid);
                        if (configDebugMessages)
                        {
                            Console.WriteLine(r.playername + " sped in pitlane");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// A racer finishes the race
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerFinish(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerFinish e)
        {
            if (configDebugMessages)
            {
                if (database.ContainsKey(getConnectionid(e.PlayerId)))
                {
                    lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                    Console.WriteLine(" ");
                    Console.WriteLine(r.playername + " crossed the finish line");
                }
            }

            // Invokes Auto Restart function
            if ((!RaceFinished) && (configAutoRestart))
            {
                AllowRestart = true;
                RaceFinished = true;
                Thread RestartCount = new Thread(new ThreadStart(DoRestartCount));
                RestartCount.Start();
                if (configDebugMessages)
                {
                    Console.WriteLine(" ");
                    Console.WriteLine("Countdown started");
                }
            }
        }

        /// <summary>
        /// Auto Restart function button programming
        /// </summary>
        public void DoRestartCount()
        {
            TimeSpan restarttime2 = new TimeSpan();
            restarttime2 = restarttime;
            while ((RaceFinished) && (configAutoRestart))
            {
                if (restarttime2.TotalSeconds == 1)
                {
                    restartbutton.Text = "Race restarting in " + restarttime2.TotalSeconds + " seconds";
                }
                else
                {
                    restartbutton.Text = "Race restarting in " + restarttime2.TotalSeconds + " seconds";
                }
                handler.ShowButton(restartbutton, false);

                restarttime2 = restarttime2.Subtract(new TimeSpan(0, 0, 1));

                if (restarttime2.TotalSeconds < 5)
                {
                    handler.SendMessage("/rcm Race Restarting!");
                    handler.SendMessage("/rcm_all");
                }

                if (restarttime2.TotalSeconds < 0)
                {
                    handler.DeleteButton(0, false);
                    handler.SendMessage("/rcc_all");
                    handler.SendMessage("/restart");
                    if (configDebugMessages)
                    {
                        Console.WriteLine(" ");
                        Console.WriteLine("Auto restarted");
                    }
                    return;
                }
                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// Function to spectate idle racers
        /// </summary>
        private void CheckRacerSpeeds()
        {
            while ((!RaceFinished) && (configSpectateStationary))
            {
                Hashtable ht = (Hashtable)this.database.Clone();
                Thread.Sleep(1000);
                //foreach (DictionaryEntry Entry in this.database)
                foreach (DictionaryEntry Entry in ht)
                {
                    lfsracer racer = (lfsracer)Entry.Value;
                    if (racer.isracing == true)
                    {
                        racer.Idle++;
                    }

                    if ((racer.Idle > configSpectateTime) && (racer.isracing == true) && (racer.Speed < 5))
                    {
                        if (demohost)
                        {
                            handler.SendMessage("/spec " + racer.playername);
                        }
                        else
                        {
                            handler.SendMessage("/spec " + racer.username);
                        }
                        handler.SendMessageToConnection(TextColor.Yellow + "Idle on track for too long", racer.connectionid);
                        if (configDebugMessages)
                        {
                            Console.WriteLine(racer.playername + " was idle on track");
                        }
                        racer.Idle = 0;
                        racer.isracing = false;
                    }
                }
            }
        }

        /// <summary>
        /// The race start event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackRaceStart(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackRaceStart e)
        {
            handler.RequestConnectionInfo();
            handler.RequestPlayerInfo();
            AllowRestart = true;
            RaceFinished = false;

            foreach (DictionaryEntry Entry in database)
            {
                lfsracer racer = (lfsracer)Entry.Value;
                racer.Idle = 0;
                racer.lapnumber = 0;
            }

            // Race Start host messages function
            if (configRaceStartMessage)
            {
                String s = q.getQuote();
                handler.SendMessage(q.getQuote());
                handler.SendMessage("Don't forget to lift the throttle before you shift! :)");
            }
            else
            {
            }

            // Auto Spectate function thread management
            if (configSpectateStationary)
            {
                Thread AutoSpecThread = new Thread(new ThreadStart(CheckRacerSpeeds));
                if (AutoSpecThread.IsAlive == true)
                {
                    AutoSpecThread.Abort();
                    AutoSpecThread.Start();
                }
                else
                {
                    AutoSpecThread.Start();
                }
            }
        }

        /// <summary>
        /// A message recieved from a player
        /// Checks for swearwords in the message
        /// Will also kick a spammer from the server
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_LFSMessage(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.Message e)
        {
            if (database.ContainsKey(e.ConnectionId))
            {
                lfsracer r = (lfsracer)database[e.ConnectionId];

                // Racer commands and help functions
                if (e.UserType == UserType.Prefix)
                {
                    if (e.MessageText.Contains("finalgear"))
                    {
                        handler.SendMessage("visit www.finalgear.com, for proper racing!");
                    }

                    // Invokes the Auto Restart function. User can only be admin.
                    if ((e.MessageText.Contains("vote")) && (configAutoRestart) && (r.isadmin == true))
                    {
                        RaceFinished = true;
                        AllowRestart = true;
                        Thread t = new Thread(new ThreadStart(DoRestartCount));
                        t.Start();
                    }

                    // Help menu for racers. Not implemented yet, placeholder.
                    if (e.MessageText.Contains("help"))
                    {
                    }

                    // Overrides all functions and restarts the race. User can only be admin.
                    if ((e.MessageText.Contains("restart")) && (r.isadmin == true))
                    {
                        AllowRestart = true;
                        RaceFinished = true;
                        handler.SendMessage("/restart");
                    }

                    // Redisplays the agreement buttons to the racer.
                    if ((e.MessageText.Contains("rules")) && (configAgreement))
                    {
                        handler.ShowButton(Agree, e.ConnectionId);
                        handler.ShowButton(DontAgree, e.ConnectionId);

                        for (int i = 0; i < this.agreementlines; i++)
                        {
                            handler.ShowButton(agreement[i], e.ConnectionId);
                        }
                    }
                }

                // Checks racer messages for swearwords
                if ((checkSwearwords(e.MessageText)) && (configCheckSwearwords))
                {
                    handler.SendMessageToConnection(TextColor.Yellow + "No swearing!", e.ConnectionId);
                    if (demohost)
                    {
                        handler.SendMessage("/kick " + r.playername);
                    }
                    else
                    {
                        handler.SendMessage("/kick " + r.username);
                    }
                    handler.SendMessage(r.playername + TextColor.Yellow + " kicked for swearing!");
                    if (configDebugMessages)
                    {
                        Console.WriteLine(r.playername + " kicked for swearing");
                    }
                }

                // Checks if a racer is spamming the server
                if (configSpamming)
                {
                    DateTime now = DateTime.Now;
                    System.TimeSpan ts = now - r.lastchat;
                    r.lastchat = now;

                    if (ts.TotalMilliseconds < 5000) //Maximum time betweeen lines to count as flood
                    {
                        if (configDebugMessages)
                        {
                            Console.WriteLine(ts.TotalMilliseconds < 5000);
                        }
                        r.chatted++;
                    }

                    else
                    {
                        if (configDebugMessages)
                        {
                            Console.WriteLine((ts.TotalMilliseconds < 5000) + " " + r.chatted);
                        }
                        r.chatted = 1;
                    }

                    if (r.chatted >= configSpammingNum)
                    {
                        if (configDebugMessages)
                        {
                            Console.WriteLine(" ");
                            Console.WriteLine(r.playername + " SPAMMING");
                        }
                        handler.SendMessage("Bye, Kicked for Spamming");
                        handler.SendMessageToConnection("Don't SPAM", r.connectionid);
                        if (demohost)
                        {
                            handler.SendMessage("/kick " + r.playername);
                        }
                        else
                        {
                            handler.SendMessage("/kick " + r.username);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// A racer connects to the server
        /// Also checks for swear words in the players name
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackConnection(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackConnection e)
        {
            if (e.ConnectionId == 0)
            {
                if (configDebugMessages)
                {
                    Console.WriteLine(e.PlayerName + " is Host");
                }
            }
            else
            {
                if (!database.ContainsKey(e.ConnectionId))
                {
                    // Prompting for agreement
                    if (configAgreement)
                    {
                        handler.ShowButton(Agree, e.ConnectionId);
                        handler.ShowButton(DontAgree, e.ConnectionId);

                        for (int i = 0; i < this.agreementlines; i++)
                        {
                            handler.ShowButton(agreement[i], e.ConnectionId);
                        }
                    }

                    lfsracer racer = new lfsracer(e.UserName, e.ConnectionId);
                    racer.playername = e.PlayerName;

                    // Check for swearwords in playername
                    if ((checkSwearwords(racer.playername)) && (configCheckSwearwords))
                    {
                        handler.SendMessageToConnection(TextColor.Yellow + "No swearwords in the name", e.ConnectionId);
                        if (configDebugMessages)
                        {
                            Console.WriteLine(" ");
                            Console.WriteLine(racer.playername + " kicked for swear in name");
                        }
                        if (demohost)
                            handler.SendMessage("/kick " + racer.playername);
                        else
                            handler.SendMessage("/kick " + racer.username);
                    }

                    racer.isadmin = e.IsAdmin;
                    racer.isremote = e.IsRemote;
                    database.Add(e.ConnectionId, racer);
                }
            }
        }

        /// <summary>
        /// A racer leaves the server
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackConnectionLeave(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackConnectionLeave e)
        {
            if (database.ContainsKey(e.ConnectionId))
            {
                database.Remove(e.ConnectionId);
            }
        }

        /// <summary>
        /// A racer joins the race/qual
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayer(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayer e)
        {
            if (database.ContainsKey(e.ConnectionId))
            {
                lfsracer racer = (lfsracer)database[e.ConnectionId];
                racer.plate = e.Plate;
                racer.playerid = e.PlayerId;
                racer.isracing = true;

                switch (allowedControllers)
                {
                    case 0:
                        break;
                    case 1:
                        if (e.KeyboardNoHelp)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("Keyboard Stabilized or better is required on this server.", e.ConnectionId);
                            controllerUsed = "Keyboard No Help";
                        }
                        break;
                    case 2:
                        if (e.KeyboardNoHelp)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", e.ConnectionId);
                            controllerUsed = "Keyboard No Help";
                        }
                        if (e.KeyboardStabilized)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", e.ConnectionId);
                            controllerUsed = "Keyboard Stabilized";
                        }
                        break;
                    case 3:
                        if (e.KeyboardNoHelp)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A wheel is required on this server.", e.ConnectionId);
                            controllerUsed = "Keyboard No Help";
                        }
                        if (e.KeyboardStabilized)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", e.ConnectionId);
                            controllerUsed = "Keyboard Stabilized";
                        }
                        if (e.Mouse)
                        {
                            if (demohost)
                            {
                                handler.SendMessage("/spec " + racer.playername);
                            }
                            else
                            {
                                handler.SendMessage("/spec " + racer.username);
                            }
                            handler.SendMessageToConnection("A mouse or better is required on this server.", e.ConnectionId);
                            controllerUsed = "Mouse";
                        }
                        break;
                }

                if ((!racer.signedAgreement) && (configAgreement))
                {
                    if (demohost)
                    {
                        handler.SendMessage("/spec " + racer.playername);
                    }
                    else
                    {
                        handler.SendMessage("/spec " + racer.username);
                    }
                }

                // handicap checker function
                if ((configHandicaps) && (h.Handicaps.ContainsKey(racer.username)))
                {
                    handicap Handicap = (handicap)h.Handicaps[racer.username];
                    if ((racer.username == Handicap.username))// && (e.HandicapIntakeRestriction != h.intake) && (e.HandicapMass != h.mass))
                    {
                        if ((e.HandicapIntakeRestriction == Handicap.intake) && (e.HandicapMass == Handicap.mass))
                        {
                        }
                        else
                        {
                            handler.SendMessage("/spec " + racer.username);
                            handler.SendMessageToConnection("You have not added your assigned handicaps.", e.ConnectionId);
                            handler.SendMessageToConnection("Your handicaps are Intake: " + Handicap.intake + " and Mass: " + Handicap.mass, e.ConnectionId);
                        }
                    }
                }

                if (configRestrictions)
                {
                    restrictions Restrictions = (restrictions)r.Restrictions[e.CarName];
                    if ((e.CarName == Restrictions.car) && (e.HandicapIntakeRestriction == (Restrictions.intake / 100)) && (e.HandicapMass == Restrictions.mass))
                    {

                    }
                    else
                    {
                        handler.SendMessage("/spec " + racer.username);
                        handler.SendMessageToConnection("You have not added the required restrictions for the car chosen.", e.ConnectionId);
                        handler.SendMessageToConnection("The requirements are Intake: " + Restrictions.intake + " and Mass: " + Restrictions.mass + " for the " + Restrictions.car, e.ConnectionId);
                    }
                }

                if (configDebugMessages)
                {
                    Console.WriteLine(racer.playername + " " + controllerUsed);
                    controllerUsed = "";
                }
            }
        }

        /// <summary>
        /// A racer leaves the race/qual
        /// Racer spectates, leaves track, DNF, leaves server
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void  handler_RaceTrackPlayerLeave(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerLeave e)
        {
            if (database.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                r.isracing = false;
                r.Idle = 0;
                r.lapnumber = 0;

                if (configDebugMessages)
                {
                    Console.WriteLine(" ");
                    Console.WriteLine(r.playername + " left the track");
                }
            }
        }

        /// <summary>
        /// A racer goes to the pits
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_RaceTrackPlayerPits(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.RaceTrackPlayerPits e)
        {
            if (database.ContainsKey(getConnectionid(e.PlayerId)))
            {
                lfsracer r = (lfsracer)database[(getConnectionid(e.PlayerId))];
                r.isracing = false;
                r.Idle = 0;
                r.lapnumber = 0;

                if (configDebugMessages)
                {
                    Console.WriteLine(" ");
                    Console.WriteLine(r.playername + " pitted");
                }
            }
        }

        /// <summary>
        /// Any LFS event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The e.</param>
        void handler_LFSState(InSimHandler sender, FullMotion.LiveForSpeed.InSim.Events.State e)
        {
            numPlayers = e.NumberOfPlayers;
            raceState = e.RaceState.ToString();
            //byte test = e.ViewedPlayerId;
        }

        /// <summary>
        /// Gets the connectionid or a player's id
        /// Returns it back to the function that called for it
        /// </summary>
        /// <param name="playerid">The playerid.</param>
        /// <returns>connectionid</returns>
        public byte getConnectionid(byte playerid)
        {
            try
            {
                foreach (System.Collections.DictionaryEntry Entry in this.database)
                {
                    lfsracer  racer = (lfsracer)Entry.Value;
                    if (racer.playerid == playerid)
                        return racer.connectionid;
                }
                return (byte)0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return (byte)0;
            }
        }

        /// <summary>
        /// Initializes the connection to the server
        /// Requests info from all racers currently on server
        /// </summary>
        public void Start()
        {
            try
            {
                Assembly SampleAssembly = Assembly.Load(Assembly.GetExecutingAssembly().FullName);
                foreach (Attribute a in SampleAssembly.GetCustomAttributes(true))
                {
                    if (a is AssemblyTitleAttribute)
                    {
                        fgTitle = (a as AssemblyTitleAttribute).Title;
                    }
                    if (a is AssemblyFileVersionAttribute)
                    {
                        fgVersion = (a as AssemblyFileVersionAttribute).Version;
                    }
                    if (a is AssemblyCompanyAttribute)
                    {
                        fgCompany = (a as AssemblyCompanyAttribute).Company;
                    }
                }

                Console.Title = fgTitle + " " + fgVersion;
                Console.WriteLine(fgTitle);
                Console.WriteLine("Version: " + fgVersion);
                Console.WriteLine(fgCompany);
                Console.WriteLine("Connecting");
                handler.Initialize(10);
                Console.WriteLine("Connected");
                handler.SendMessage("Race Control System Restarting...");
                handler.SendMessage(fgTitle);
                handler.SendMessage("Version: " + fgVersion);
                handler.SendMessage(fgCompany);
                handler.RequestConnectionInfo();
                handler.RequestPlayerInfo();
            }
            catch (Exception ex)
            {
                Console.WriteLine();
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.Source);
                Console.WriteLine(fgTitle + " could not connect. Please check your ip, port and admin pass settings.");
                Console.ReadLine();
                Environment.Exit(0);
            }
        }

        /// <summary>
        /// Gooooes this instance.
        /// </summary>
        public void Goooo()
        {
            //int counter = 0;
            while (handler.State == InSimHandler.HandlerState.Connected)
            {
                Thread.Sleep(1); // Keep CPU usage low

                /* counter++;
                if (counter < 500)
                    Console.WriteLine(counter);
                else
                    return;*/

            }
        }

        /// <summary>
        /// Loads the swearwords from the swearwords.txt file into a hashtable
        /// </summary>
        public void LoadSwearwords()
        {
            if (configCheckSwearwords)
            {
                try
                {
                    StreamReader re = File.OpenText("swearwords.txt");
                    string input = null;
                    while ((input = re.ReadLine()) != null)
                    {
                        swearwords.Add(input);
                    }
                    re.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine();
                    Console.ReadLine();
                    Environment.Exit(0);
                }
            }
        }

        /// <summary>
        /// Checks the swearwords in a message from a player forwarded by the message function
        /// </summary>
        /// <param name="s">String s</param>
        /// <returns></returns>
        public Boolean checkSwearwords(String s)
        {
            if (configCheckSwearwords)
            {
                char[] separators = { ' ' };
                string[] temp = s.Split(separators, 10000);
                foreach (String t in temp)
                {
                    foreach (String swear in swearwords)
                    {
                        if (swear.Equals(t.ToLower()))
                        {
                            if (configDebugMessages)
                            {
                                Console.WriteLine(" ");
                                Console.WriteLine("SWEAR: " + s);
                            }
                            return true;
                        }
                    }
                }
            }
            return false;
        }
    }
}
